/******************************************************
 * @file fb.c
 * @author Destiny 
 * @brief fb设备模块
 * 
 * @version 0.1
 * @date 2024-07-18
 *****************************************************/
#include <config.h>
#include <disp_manager.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>



static int FBDeviceInit(void);         /*fb设备初始化函数*/
static int FBShowPixel(int iX, int iY, uint32_t dwColor);  /*打点*/
static int FBCleanScreen(uint32_t dwBackColor);            /*清屏*/
static int FBShowPage(PT_VideoMem ptVideoMem);                 /*显示缓冲区写入设备显存*/


static int g_iFd;     /*显示设备节点描述符*/

static struct fb_var_screeninfo g_tFBVar;    /*fb设备可变参数*/
static struct fb_fix_screeninfo g_tFBFix;	 /*fb设备固定参数*/	
static uint8_t *g_pucFBMem;            /*fb设备显存首地址*/	
static uint32_t g_dwScreenSize;          /*fb设备显存大小*/	

static uint32_t g_dwLineWidth;           /*fb设备一行占字节数*/	
static uint32_t g_dwPixelWidth;			 /*fb设备像素宽度*/	
/*fb设备描述结构体*/
static T_DispOpr g_tFBOpr = {
	.name        = "fb",
	.DeviceInit  = FBDeviceInit,
	.ShowPixel   = FBShowPixel,
	.CleanScreen = FBCleanScreen,
	.ShowPage    = FBShowPage,
};

/*
 * @description		: FB设备初始化函数
 * @param - 	   	: 无
 * @return 			: 0 成功;其他 失败
 */
static int FBDeviceInit(void)
{
	int ret;
	/*打开指定FB设备节点，得到对应文件描述符*/
	g_iFd = open(FB_DEVICE_PATH, O_RDWR);
	if (0 > g_iFd)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't open %s\n", FB_DEVICE_PATH);
	}
	/*读取设备可变参数*/
	ret = ioctl(g_iFd, FBIOGET_VSCREENINFO, &g_tFBVar);
	if (ret < 0)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't get fb's var\n");
		return -1;
	}
	/*读取设备不变参数*/
	ret = ioctl(g_iFd, FBIOGET_FSCREENINFO, &g_tFBFix);
	if (ret < 0)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't get fb's fix\n");
		return -1;
	}
	/*计算显存大小并通过mmap映射到进程地址空间*/
	g_dwScreenSize = g_tFBVar.xres * g_tFBVar.yres * g_tFBVar.bits_per_pixel / 8;
	g_pucFBMem = (uint8_t *)mmap(NULL , g_dwScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, g_iFd, 0);
	if (0 > g_pucFBMem)	
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't mmap\n");
		return -1;
	}
	/*构造描述结构体*/
	g_tFBOpr.iXres       = g_tFBVar.xres;
	g_tFBOpr.iYres       = g_tFBVar.yres;
	g_tFBOpr.iBpp        = g_tFBVar.bits_per_pixel;
	g_tFBOpr.iLineWidth  = g_tFBVar.xres * g_tFBOpr.iBpp / 8;
	g_tFBOpr.pucDispMem  = g_pucFBMem;
	DBG_PRINTF(DLOG_LVL_DEBUG,DLOG_TAG,"iXres:%d iYres:%d iBpp:%d\n",g_tFBOpr.iXres,g_tFBOpr.iYres,g_tFBOpr.iBpp);
	g_dwLineWidth  = g_tFBVar.xres * g_tFBVar.bits_per_pixel / 8;
	g_dwPixelWidth = g_tFBVar.bits_per_pixel / 8;
	
	return 0;
}
/*
 * @description		: FB设备指定坐标设置颜色
 * @param - iX,iY	: 坐标
 * @param - dwColor	: RGB888颜色参数
 * @return 			: 0 成功;其他 失败
 */
static int FBShowPixel(int iX, int iY, uint32_t dwColor)
{
	uint8_t *pucFB;
	uint16_t *pwFB16bpp;
	uint32_t *pdwFB32bpp;
	uint16_t wColor16bpp; 
	int iRed;
	int iGreen;
	int iBlue;

	if ((iX >= g_tFBVar.xres) || (iY >= g_tFBVar.yres))  //是否超出范围
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"out of region\n");
		return -1;
	}
	/*计算坐标的地址*/
	pucFB      = g_pucFBMem + g_dwLineWidth * iY + g_dwPixelWidth * iX;
	pwFB16bpp  = (uint16_t *)pucFB;
	pdwFB32bpp = (uint32_t *)pucFB;
	/*颜色数据转化*/
	switch (g_tFBVar.bits_per_pixel)
	{
		case 8:
		{
			*pucFB = (uint8_t)dwColor;
			break;
		}
		case 16:
		{
			
			iRed   = (dwColor >> (16+3)) & 0x1f;
			iGreen = (dwColor >> (8+2)) & 0x3f;
			iBlue  = (dwColor >> 3) & 0x1f;
			wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
			*pwFB16bpp	= wColor16bpp;
			break;
		}
		case 32:
		{
			*pdwFB32bpp = dwColor;
			break;
		}
		default :
		{
			DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't support %d bpp\n", g_tFBVar.bits_per_pixel);
			return -1;
		}
	}

	return 0;
}

/*
 * @description			: 将一页显示缓冲区刷入FB设备显存中
 * @param - ptVideoMem	: 缓冲区结构体指针
 * @return 				: 0 成功;其他 失败
 */
static int FBShowPage(PT_VideoMem ptVideoMem)
{
	memcpy(g_tFBOpr.pucDispMem, ptVideoMem->tPixelDatas.aucPixelDatas, ptVideoMem->tPixelDatas.iTotalBytes);
	return 0;
}
/*
 * @description			: FB屏幕设置为指定颜色
 * @param - dwBackColor	: RGB888颜色参数
 * @return 				: 0 成功;其他 失败
 */
static int FBCleanScreen(uint32_t dwBackColor)
{
	uint8_t *pucFB;
	uint16_t *pwFB16bpp;
	uint32_t *pdwFB32bpp;
	uint16_t wColor16bpp; 
	int iRed;
	int iGreen;
	int iBlue;
	int i = 0;

	pucFB      = g_pucFBMem;
	pwFB16bpp  = (uint16_t *)pucFB;
	pdwFB32bpp = (uint32_t *)pucFB;

	switch (g_tFBVar.bits_per_pixel)
	{
		case 8:
		{
			memset(g_pucFBMem, dwBackColor, g_dwScreenSize);
			break;
		}
		case 16:
		{
			iRed   = (dwBackColor >> (16+3)) & 0x1f;
			iGreen = (dwBackColor >> (8+2)) & 0x3f;
			iBlue  = (dwBackColor >> 3) & 0x1f;
			wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
			while (i < g_dwScreenSize)
			{
				*pwFB16bpp	= wColor16bpp;
				pwFB16bpp++;
				i += 2;
			}
			break;
		}
		case 32:
		{
			while (i < g_dwScreenSize)
			{
				*pdwFB32bpp	= dwBackColor;
				pdwFB32bpp++;
				i += 4;
			}
			break;
		}
		default :
		{
			DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"can't support %d bpp\n", g_tFBVar.bits_per_pixel);
			return -1;
		}
	}

	return 0;
}
/*
 * @description			: FB设备初始化
 * @param - 			: 无
 * @return 				: 无
 */
int FBInit(void)
{
	return RegisterDispOpr(&g_tFBOpr);
}